#ifndef _CAD_SLICER_SLICE_H_
#define _CAD_SLICER_SLICE_H_

#include <map>
#include <forward_list>

#include <prenstar/assert.h>

#include <cad-slicer/geometry.h>


namespace cadslicer
{

class slicer;

class slice
{
public:

  friend class slicer;

  class hierarchy;

  /// @brief Return the contours of the intersection between
  ///        the current slice and sliced object.
  contours const& get_contours(void) const
  { return _contours; }

  /// @brief Bounding box of each contour.
  ///
  /// Format {x_min, x_max, y_min, y_max}.
  /// The element at i is the bounding box of the contour i.
  bounding_boxes const& get_bounding_boxes(void) const
  { return _boxes; }

  /// @brief The inclusion hierachy between contours.
  ///
  /// @warning The first element of this tree doesn't have a
  ///          valid index. It is just used to link the independant parts.
  hierarchy const& get_contour_hierachy(void) const
  { return _hierachy; }

  /// @brief Return the altitude of the slice.
  float get_z(void) const { return _z; }

  class hierarchy
  {
  public:

    friend class ::cadslicer::slice;

    typedef std::forward_list<hierarchy*> children;

    children const& get_children(void) const
    { return _children; }

    hierarchy const* get_parent(void) const
    { return _parent; }

    std::forward_list<unsigned> all_children(void) const
    {
      std::forward_list<unsigned> result;
      result.push_front(_index);
      for (hierarchy const* h : _children)
        result.merge(h->all_children());
      return result;
    }

    operator unsigned(void) const { return _index; }

  private:

    hierarchy(void)
      : _index(std::numeric_limits<unsigned>::max())
      , _parent(nullptr) {}

    hierarchy(hierarchy& parent, unsigned index)
      : _index(index)
      , _parent(&parent) {}

    hierarchy(hierarchy const&) = delete;

    ~hierarchy(void)
    { for (hierarchy* child : _children) delete child; }

    unsigned _index;
    hierarchy* _parent;
    std::forward_list<hierarchy*> _children;
  };

private:

  void set_z(float z) { _z = z; }

  void init_faces(void) {}
  void finish_faces(void)
  {
    _to_add.clear();
    _to_remove.clear();
  }

  void update_faces_for_current(faces& fs)
  { fs.insert(_to_add.begin(), _to_add.end()); }
  void update_faces_for_next(faces& fs)
  { for (auto const& face : _to_remove) fs.erase(face); }

  void add_face(mesh m, face f)
  { _to_add.insert(face_id(m,f)); }
  void remove_face(mesh m, face f)
  { _to_remove.insert(face_id(m,f)); }

  void init_edges(unsigned size) {}
  void finish_edges(void);

  std::pair<bool, triangle_id>
  has_face(vertex_id v1, vertex_id v2, vertex_id v3)
  {
    triangle_id triangle(v1, v2, v3);
    triangle.sort();
    bool ok = (_intersections.count(triangle) != 0);
    return std::make_pair(ok, triangle);
  }
  void add_edge(point2f&& pt1, point2f&& pt2,
                triangle_id&& vertice, triangle_id&& sorted)
  {
    auto it = _intersections.emplace(
          std::forward<triangle_id>(sorted),
          triangle_intersection(std::forward<point2f>(pt1),
                                std::forward<point2f>(pt2),
                                std::forward<triangle_id>(vertice)));
    prenstar_assert_true(it.second);

    declare_edge(
          it.first->second.first,
          &it.first->second);
    declare_edge(
          it.first->second.second,
          &it.first->second);
  }
  void declare_edge(edge_id const& id, triangle_intersection* where)
  {
    if (_neighbours.count(id))
    {
      prenstar_assert_nullptr(_neighbours[id].second);
      _neighbours[id].second = where;
    }
    else
    {
      _neighbours[id].first = where;
      prenstar_assert_nullptr(_neighbours[id].second);
    }
  }

  void check_neighbours(void);
  void check_intersections(void);

  bool insert_in_hierarchy(hierarchy* hier, unsigned idx);

  float _z;

  faces _to_add;
  faces _to_remove;

  std::map<triangle_id, triangle_intersection> _intersections;
  std::map<edge_id, triangle_neighbour> _neighbours;

  contours _contours;
  bounding_boxes _boxes;
  hierarchy _hierachy;
};

} // cadslicer

#endif // _CAD_SLICER_SLICE_H_
